package Market;

import java.util.ArrayList;
import java.util.Hashtable;

import edu.iastate.jrelm.core.SimpleAction;
import edu.iastate.jrelm.rl.rotherev.REPolicy;
import edu.iastate.jrelm.core.JReLMAgent;
import edu.iastate.jrelm.rl.ReinforcementLearner;
import edu.iastate.jrelm.rl.SimpleStatelessLearner;
import edu.iastate.jrelm.rl.rotherev.variant.VREParameters;


import sim.engine.*;


public class GenCo implements Steppable {
	
	private static final long serialVersionUID = 6628525522939315274L;
													/** Twenty-four hours */
	private static final int Hours = 24;

// Solutions from Optimal Power Flow
													/** Hourly power commitment quantity (double array) */
	private double[][] CommitmentD;  
													/** Hourly power commitment quantity (array) */
	//private double[] CommitmentA;   
													/** Hourly locational marginal price (array) */
	//private double[] LMPA;  
													/** Hourly locational marginal price (double array)  */
	//private double[][] LMPD;	
													/** Hourly Total Cost = totalVariableCost + FCost  */
	
	private double HourlyTotalCost;          
													/**  Hourly Variable Cost[h] = a*power + b*power^2 */
	private double HourlyVariableCost;
													/** Hourly Net Earning[h] = dispatch[h]*lmp[h]  */
	private double HourlyNetEarning;
													/** Hourly Profit[h] = dispatch[h]*lmp[h] - HourlyTotalCost[h]  */
	private double HourlyProfit;
													/** Daily Net Earnings = sum of HourlyNetEarning over 24 hours  */
	private double  DailyNetEarnings;   
													/** dailyProfit = sum of HourlyProfit over 24 hours   */
	private double DailyProfit;   
													/** hourlyRevenue[h]  = Commitment[h] * LMP[h];  */
	private double HourlyRevenue;
													/** Sum of 24 hourly Revenues */
	private double DailyRevenue;  
													/** Fixed cost of production  */
	private double FixedCost;
													/** GenCo's accumulative money holding, money (new) = money(previous) + DailyProfit(new) (double array)*/
	private double[] GenCoMoneyD;
													/**  GenCo's accumulative money holding, money (new) = money(previous) + DailyProfit(new) (array)*/ 
	public double genCoMoney;
													/** GenCo's Data: {Gen ID, at Node, FCost, aT, bT, capTL, capTU, Initial Money in pounds} */
	private double[][] GenCoData;   
													/** Generators' locations {gen1 at node 1, gen2 at node3,.., geni at nodej} */
	private int GenCoAtNode;

// Learning parameters
													/** GenCo's learning choice probability  */
	private double ChoiceProbability; 
													/** GenCo's learning choice propensity  */
	private double ChoicePropensity;   
													/** GenCo's learning choice ID  */
	private int    ChoiceID;   
													/** GenCo's propensity */
	double propensity;
													/** Random Seed */ 
	int randomSeed;  
													/** cooling parameter that affects the degree to which Generator i makes use of propensity values in determining its choice probabilities  <br> @see DynTestAMES.JSLT.pdf*/
	double cooling;
													/** The introduction of the recency parameter in (22) acts as a damper on the growth of the propensities over time <br> @see DynTestAMES.JSLT.pdf (22) */
	double recency;
													/** Theexperimentation parameter in (23) permits reinforcement to spill over to some extent from a chosen supply offer to other <br> supply offers to encourage continued experimentation with various supply offers in the early <br> stages of the learning process <br> @see DynTestAMES.JSLT.pdf (23) */
	double experimentation;
													/** 0->profit, 1->net earnings */ 
	double rewardSelection;
													/** Slope-start parameter <br> @see DynTestAMES.JSLT.pdf (pg 33) */
	private double slopeStart;

// Supply offer	
													/** Reported cost coefficient ($/MWh) <br> @see DynTestAMES.JSLT.pdf  */
	private double aR;
													/** Reported cost coefficient ($/MW^2h) <br> @see DynTestAMES.JSLT.pdf  */
	private double bR;
													/** Reported lower production limit (MWs) <br> @see DynTestAMES.JSLT.pdf */
	private double capRL;
													/** Reported  upper production limit (MWs) <br> @see DynTestAMES.JSLT.pdf */
	private double capRU;
													/** Reported supply offer (aR,bR,CapRL,CapRU) <br> @see DynTestAMES.JSLT.pdf*/
	private double[] RSupplyOffer;   	
													/** True cost coefficient ($/MWh) <br> @see DynTestAMES.JSLT.pdf   */
	private double aT; 
													/** True cost coefficient ($/MW^2h) <br> @see DynTestAMES.JSLT.pdf  */
	private double bT; 
													/** True lower production limit (MWs) <br> @see DynTestAMES.JSLT.pdf */
	private double capTL;
													/** True  upper production limit (MWs) <br> @see DynTestAMES.JSLT.pdf */
	private double capTU;
													/** True supply offer (a,b,capTL,capTU)  <br> @see DynTestAMES.JSLT.pdf */
	private double[] TSupplyOffer;  

													/** l = aT+2*bT*capTL  <br> @see DynTestAMES.JSLT.pdf (28)*/
	private double l;
													/** u = aT+2*bT*CapRU <br> @see DynTestAMES.JSLT.pdf (29) */
	private double u;
													/** lR = l/(1-RIL) <br> @see DynTestAMES.JSLT.pdf (30) */
	private double lR;
													/** uR = us/(1-RIU) <br> @see DynTestAMES.JSLT.pdf (32) */
	private double uR;
													/** if u>lR : uS = u  else: uS = lR + slopestart <br> @see DynTestAMES.JSLT.pdf (31)*/
	private double uS;
													/** RCapL = capTL/capTU <br> @see DynTestAMES.JSLT.pdf (35) */
	double RCapL;
													/** RCapU belongs to [RIMinC,1] and rIMinC Range-index parameter for Action domain construction @see DynTestAMES.JSLT.pdf */
	double RCapU;
													/** RIL =< rIMaxL < 1 @see DynTestAMES.JSLT.pdf */
	double RIL;
													/** RIU =< rIMaxU < 1  @see DynTestAMES.JSLT.pdf*/
	double RIU;
													/** Cardinality of the action domain for GenCo  <br> @see DynTestAMES.JSLT.pdf */ 
	double m1;
													/** Cardinality of the action domain for GenCo  <br> @see DynTestAMES.JSLT.pdf */
	double m2;
													/** Cardinality of the action domain for GenCo  <br> @see DynTestAMES.JSLT.pdf */
	double m3;
													/** M = m1 * m2 * m3 */
	double M;
													/** Range-index parameter for Action domain construction  <br> @see DynTestAMES.JSLT.pdf */
	double rIMaxL;
													/** Range-index parameter for Action domain construction  <br> @see DynTestAMES.JSLT.pdf */
	double rIMaxU;
													/** rIMinC Range-index parameter for Action domain construction  <br> @see DynTestAMES.JSLT.pdf  */
	double rIMinC;
													/** Reduced-form versions of the admissible percentage supply offers <br> @see DynTestAMES.JSLT.pdf Eq(36) */
	private double[] Alpha;	
													/** Boolean used to separate GenCo's methods execution */
	
	public boolean Switch;
													/** Number of GenCos */
	private int I;
													/** Current GenCo */
	private int ii;
													/** Current day */
	public int day;	
	
													/** Action domain list*/
	private ArrayList ADList;
													/** List of learners */  
	private ArrayList LearnerList;

	private Initials.InitGenCo initgenco;

	private SimpleStatelessLearner Learner;

	private Market market;
	
	public String id;
	public int node;
	public double fcost;
	
	public int ID;
	
	public Hashtable<String, Double> supplyOffer = new Hashtable<String, Double>();
	public Hashtable<String, Double> learningData = new Hashtable<String, Double>();
	public Hashtable<String, Double> reportedSupplyOffer = new Hashtable<String, Double>();
	public Hashtable<String, Double> commitment = new Hashtable<String, Double>();
	public Hashtable<String, Double> lmpNode = new Hashtable<String, Double>();

	private double commitmentD;
	private double LMPD;
	private VREParameters learningParams;
	private SimpleStatelessLearner learner;
	
//C-O-N-S-T-R-U-C-T-O-R-1-----------------------------------------------------------------------------------------------------------------------------  
	/** This constructor is used to generate supply offer */
	public GenCo(){		
	}
	
	public GenCo(String id, int node, double fcost, double money, Market mt, Hashtable supplyoffer,Hashtable learningdata){
		
		this.id           = id;
		this.node         = node;
		this.supplyOffer  = supplyoffer;
		this.learningData = learningdata;
		this.genCoMoney   = money;
		this.fcost        = fcost;
		this.market       = mt;

		
		aT    = supplyOffer.get("aT");
		bT    = supplyOffer.get("bT");
		capTL = supplyOffer.get("capTL");
		capTU = supplyOffer.get("capTU");
		
		propensity  	  = learningData.get("propensity");
		cooling  		  = learningData.get("cooling");
		recency           = learningData.get("recency");
		experimentation   = learningData.get("experimentation");
		m1  			  = learningData.get("m1");
		m2  			  = learningData.get("m2");
		m3  			  = learningData.get("m3");
		rIMaxL   		  = learningData.get("rIMaxL");
		rIMaxU   		  = learningData.get("rIMaxU");
		rIMinC  		  = learningData.get("rIMinC");
		slopeStart  	  = learningData.get("slopeStart");
		rewardSelection   = learningData.get("rewardSelection");
		randomSeed        = -890999481;
		
    	DailyProfit       = 0;
    	DailyNetEarnings  = 0;
    	DailyRevenue      = 0;
	}
	
	public void initiateLearner()
	{
		learningParams = new VREParameters(cooling, experimentation, propensity, recency, randomSeed);	    	
		ADList = ActionDomainConstruction();
		learner = new SimpleStatelessLearner(learningParams, ADList);
		double[] triplet = (double[]) learner.chooseActionRaw();
		aR    = triplet[0];
		bR    = triplet[1];        
		capRU = triplet[2];
		capRL = capTL; 
		reportedSupplyOffer.put("aR", aR);
		reportedSupplyOffer.put("bR", bR);
		reportedSupplyOffer.put("capRU", capRU);
		reportedSupplyOffer.put("capRL", capRL);
		Learner = learner;
	}
	
	public void updateGenCoData()
	{		
		//System.out.println(id+ " is running true");
		
		Hashtable<String, Double> lmpatnode = new Hashtable<String, Double>();
		for (int hour=0; hour<market.Hours; hour++){
					
			lmpatnode =(Hashtable)market.lmpAtHour.get("lmpathour"+hour);
    			
			updateProfit(hour, lmpatnode);				  			
		}
   				
	}
	
//-----------------------------------------------------------------------------------------------------------------------------------------
    public void step(SimState state)
    {     	
    	if (state.schedule.getSteps()<=1)
    	{   	
    	initiateLearner();
    	}
    	else
    	{ 
    		aggregateFunction();
    	}
   	}		
 
//-----------------------------------------------------------------------------------------------------------------------------------------
    public void aggregateFunction(){
		
    	updateGenCoData();
		updateMoney();
		//learn();
    	
    }
    
//------------------------------------------------------------------------------------------------------------------------------------------    
    /** The function checks whether reported supply offer is feasible or not 
     * @see <f/> DynTestAMES.JSLT.pdf (pg 34)  */    
    private void ReportedSupplyOfferValidation(double x, double y, double z) {
   	
    	RCapL = capTL/capTU;
    	
//Step 0
    	capRL = capTL;
    	capRL = RCapL*capTU;
    	capRU = RCapU*(capTU-capTL)+capTL;  

    	l = aT+2*bT*capTL;
    	u = aT+2*bT*capRU;

//Step 1
    	lR = l/(1-RIL);
   
//Step2
    	if(u > lR) { uS = u; }
	   
    	else {uS = lR+slopeStart;}
	  
//Step3
    	uR = uS/(1-RIU);
   
//Step4
    	bR = 0.5*(uR-lR)/(capRU-capRL);
   
//Step5
    	aR = lR-2*bR*capTL;
   
    	System.out.println("a_reported = " + aR);
    	System.out.println("b_reported = " + bR);
    }
    
//------------------------------------------------------------------------------------------------------------------------------------------    
    /** Constructs GenCo's action domain */
    private ArrayList ActionDomainConstruction(){

    	ArrayList ADMat = new ArrayList();
    
    	M = m1 * m2 * m3;

    	double inc1 = rIMaxL/(m1 - 1);
    	double inc2 = rIMaxU/(m2 - 1);
		double inc3 = (1-rIMinC)/(m3-1);
	
		for (double i = 0; i <= rIMaxL; i = i+inc1){
			for (double j = 0; j <= rIMaxU; j = j+inc2 ){
				for(double k = 1-rIMinC; k >= 0; k = k-inc3){  
				
					Alpha = new double[3]; // initialize triplet to zeros
					Alpha[0] = i; 
					Alpha[1] = j; 
	            	Alpha[2] = k +rIMinC ; 
	            
	            	ADMat.add(Alpha);
	            		
				}		
    		}
		}
	
		return ADMat;
    }

//------------------------------------------------------------------------------------------------------------------------------------------    	
    /** Sets reported supply offer into array */
    public void SetRSupplyOffer(){

    	RSupplyOffer = new double[4];	
			RSupplyOffer[0] = aR;
			RSupplyOffer[1] = bR;
			RSupplyOffer[2] = capRL;
			RSupplyOffer[3] = capRU;	
    }
    
//------------------------------------------------------------------------------------------------------------------------------------------    	
    /** Sets true supply offer into array */
    public void setTrueSupplyOffer(){

    	TSupplyOffer = new double[4];	
    		TSupplyOffer[0]=aT;
    		TSupplyOffer[1]=bT;
    		TSupplyOffer[2]=capTL;
    		TSupplyOffer[3]=capTU;   
    }
	
//------------------------------------------------------------------------------------------------------------------------------------------    	
    /** Updates resuts of energy trading (profit, costs, etc...) */
    public void updateProfit(int h, Hashtable lmpatnode){

    	commitmentD = commitment.get("commit"+h);
   				
    	Hashtable<String, Double> lmphash = new Hashtable<String, Double>();
    	lmphash =(Hashtable)market.lmpAtHour.get("lmpathour"+h);   		
    	LMPD = (Double)lmphash.get("lmp"+node).doubleValue();  // "lmp"+node makes sure that genco matches node

    


  
    	HourlyVariableCost  = aT * commitmentD + bT * commitmentD  * commitmentD ;
    	HourlyTotalCost  = HourlyVariableCost + FixedCost;
    	HourlyRevenue  = commitmentD *LMPD;
      
    	HourlyProfit = HourlyRevenue - HourlyTotalCost;
    	HourlyNetEarning = HourlyRevenue - HourlyVariableCost;
        
    	DailyProfit += HourlyProfit;
    	DailyNetEarnings += HourlyNetEarning;
    	DailyRevenue += HourlyRevenue;
    		
    	//System.out.println(id+"  "+"DailyProfit= "+DailyProfit);	
    	//System.out.println(" HourlyProfit=  "+HourlyProfit+" HourlyRevenue=  "+HourlyRevenue+" HourlyTotalCost=  "+HourlyTotalCost );
    
    }

//------------------------------------------------------------------------------------------------------------------------------------------    	
    /** Updates GenCo's accumulative money holding after energy trade*/
    private void updateMoney(){
    	 if (day == 0){
    		 	
    		 market.genCoMoney = genCoMoney + DailyProfit;
    		//System.out.println(id+"  "+"genCoMoney= "+market.genCoMoney);
    	 }
    	 else
    	 {
    		 market.genCoMoney = market.genCoMoney+DailyProfit;
    	 }
		
    	 System.out.println(id+"  "+"genCoMoney= "+market.genCoMoney);
    }
    
//------------------------------------------------------------------------------------------------------------------------------------------    	   
      /** GenCo learning (updating propensity based on current period DailyProfit) */
	  private void learn(){
	    //System.out.println("Learning Report for GenCo: " + getID() );
	    SimpleAction lastAction = (SimpleAction) Learner.getPolicy().getLastAction();
	    ChoiceID = lastAction.getID();
	    double[] act = (double[]) lastAction.getAct();
	    REPolicy policy = (REPolicy)Learner.getPolicy();
	
	    System.out.printf("\tLast action chosen:  id= " + id +
	                       ";\t(lowerRI, upperRI, upperRCap)=(%1$6.4f, %2$6.4f, %3$6.4f)\n",
	                       act[0], act[1], act[2]);
	    
	    if(Double.isNaN(policy.getProbability(id))){
	        System.out.printf("\tBefore update --> the policy.getProbability return value is not a number!!!\n");

	        System.out.printf("\tBefore updating with daily profit: probability=%1$6.4f\tpropensity=%2$f\n",
	                         1.0,policy.getPropensity(id));
	    }
	    else
	        System.out.printf("\tBefore updating with daily profit: probability=%1$6.4f\tpropensity=%2$f\n",
	                         policy.getProbability(id),policy.getPropensity(id));
	 
	    if(rewardSelection==0)// profit
	    Learner.update(new Double(DailyProfit));

	    if(rewardSelection==1)// net earnings
	    	Learner.update(new Double(DailyNetEarnings));
	
	    if(Double.isNaN(policy.getProbability(id))){
	        System.out.printf("\tAfter update --> the policy.getProbability return value is not a number!!!\n");
	        
	        System.out.printf("\tAfter updating with daily profit:  probability=%1$6.4f\tpropensity=%2$f\n\n",
	                         1.0,policy.getPropensity(id));
	    }
	    else
	        System.out.printf("\tAfter updating with daily profit:  probability=%1$6.4f\tpropensity=%2$f\n\n",
	                         policy.getProbability(id),policy.getPropensity(id));
	    
	
	    ChoiceProbability = policy.getProbability(ChoiceID);
	    ChoicePropensity=policy.getPropensity(ChoiceID);

	  }

//------------------------------------------------------------------------------------------------------------------------------------------    	   
	/** Initiates updates of GenCo's profits, money holding and launches the learning process */
    public void updateSupplyOffer(){
		    //updateProfit(j);

		   // if(flag != 1){
		  //    updateMoney();
		   // }

		  //  learn();
		    
		   // iDayCount++;
		  /*  if(bActionProbabilityCheck&&(day>=0)){
		        updateActionProbabilities();
		    }
		    
		    iLearningCheckDayCount++;
		    if(bLearningCheck&&(iLearningCheckDayCount>=iLearningCheckStartDay)){
		        updateLearningResult();
		    }
		    
		    iDailyNetEarningDayCount++;
		    if(bDailyNetEarningThreshold&&(iDailyNetEarningDayCount>=iDailyNetEarningstartDay)){
		        updateDailyNetEarningResult();
		    } */
		  }

//------------------------------------------------------------------------------------------------------------------------------------------    	   
	/** Returns learner */
    public SimpleStatelessLearner getLearner(){
	   System.out.println(" get Learner from genco "); 
	   return Learner;
	}
} 

   













